package com.sojson.config.task;

import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/**
 * 定时任务线程池配置类
 * 
 * @author liu
 * @date 2021-04-28
 */
@Configuration
/**开启异步调用*/
@EnableAsync
/** 开启定时任务 */
@EnableScheduling
public class TaskPoolConfig {

    /**
     * 并行任务使用策略: 多线程处理<BR/>
     * destroyMethod = "shutdown"(销毁Bean的时候执行的回调方法,猜是反射机制)
     * 
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskScheduler taskSchedulerDefault() {
        // 创建一个线程池调度器,ThreadPoolTaskScheduler专门用来处理定时任务
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        // 线程名前缀
        scheduler.setThreadNamePrefix("task-default-");
        // 设置线程池容量
        scheduler.setPoolSize(20);
        // 设置任务等待时间,如果超过该值还没有销毁就强制销毁,确保最后关闭(单位: 秒)
        scheduler.setAwaitTerminationSeconds(60);
        // 任务完成再关闭线程池
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 设置当任务被取消的同时从当前调度器移除的策略
        scheduler.setRemoveOnCancelPolicy(true);
        return scheduler;
    }

    /**
     * 名称taskExecutor为普通线程的默认线程池(springboot会去找这个名字的线程池)
     * 并行任务使用策略: 多线程处理<BR/>
     * destroyMethod = "shutdown"(销毁Bean的时候执行的回调方法,猜是反射机制)
     * 
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor taskExecutor() {
        /**
         * 1.如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务
         * 2.如果此时线程池中的数量等于corePoolSize,但是缓冲队列 queueCapacity未满,那么任务被放入缓冲队列
         * 3.如果此时线程池中的数量大于corePoolSize,缓冲队列queueCapacity满,并且线程池中的数量小于maxPoolSize,建新的线程来处理被添加的任务
         * 4.如果此时线程池中的数量大于corePoolSize,缓冲队列queueCapacity满,并且线程池中的数量等于maxPoolSize
         *   那么通过handler所指定的策略来处理此任务.
         *   也就是: 处理任务的优先级为: 核心线程corePoolSize,任务队列workQueue,最大线程 maxPoolSize,如果三者都满了,使用handler处理被拒绝的任务
         * 5.当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止.这样,线程池可以动态的调整池中的线程数
         */

        // 创建一个线程池调度器,ThreadPoolTaskExecutor用来处理普通任务
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 线程名前缀
        executor.setThreadNamePrefix("main-default-");
        // 线程池维护线程的最小数量,表示线程池核心线程,正常情况下开启的线程数量
        executor.setCorePoolSize(4);
        // 队列最大容量,当核心线程都在跑任务,还有多余的任务会存到此处
        executor.setQueueCapacity(100);
        // 最大线程数,如果queueCapacity存满了,还有任务就会启动更多的线程,直到线程数达到
        executor.setMaxPoolSize(200);
        // 空闲线程的存活时间(单位: 秒)
        executor.setKeepAliveSeconds(60);
        /**
         * 当任务数量超过MaxPoolSize和QueueCapacity时使用的策略,该策略是又调用任务的线程执行</BR>
         * AbortPolicy:         默认策略,直接抛出异常,不再执行
         * CallerRunsPolicy:    简单点说就是后面排队的线程就在那儿等着
         *                      被拒绝的任务在主线程中运行,所以主线程就被阻塞了,别的任务只能在被拒绝的任务执行完之后才会继续被提交到线程池执行
         * DiscardPolicy:       直接丢弃此线程,不会抛异常也不会执行
         * DiscardOldestPolicy: 会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去(不管之前的任务有没有执行结束)
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }

}